package com.haohan.cloud.scm.goods.core.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.haohan.cloud.scm.api.constant.ScmCacheNameConstant;
import com.haohan.cloud.scm.api.constant.enums.goods.GoodsStatusEnum;
import com.haohan.cloud.scm.api.constant.enums.opc.YesNoEnum;
import com.haohan.cloud.scm.api.goods.dto.GoodsDTO;
import com.haohan.cloud.scm.api.goods.dto.GoodsExtDTO;
import com.haohan.cloud.scm.api.goods.dto.GoodsModelDTO;
import com.haohan.cloud.scm.api.goods.dto.GoodsSqlDTO;
import com.haohan.cloud.scm.api.goods.entity.*;
import com.haohan.cloud.scm.api.goods.req.manage.EditGoodsReq;
import com.haohan.cloud.scm.api.goods.req.manage.GoodsListReq;
import com.haohan.cloud.scm.api.goods.req.manage.GoodsPricingReq;
import com.haohan.cloud.scm.api.goods.trans.GoodsTrans;
import com.haohan.cloud.scm.api.goods.vo.GoodsVO;
import com.haohan.cloud.scm.api.manage.dto.ShopExtDTO;
import com.haohan.cloud.scm.api.manage.entity.Merchant;
import com.haohan.cloud.scm.api.manage.entity.Shop;
import com.haohan.cloud.scm.api.supply.entity.SupplierGoods;
import com.haohan.cloud.scm.api.supply.trans.SupplyGoodsTrans;
import com.haohan.cloud.scm.common.tools.exception.ErrorDataException;
import com.haohan.cloud.scm.goods.core.GoodsCategoryCoreService;
import com.haohan.cloud.scm.goods.core.GoodsCoreService;
import com.haohan.cloud.scm.goods.service.*;
import com.haohan.cloud.scm.goods.utils.ScmGoodsUtils;
import lombok.AllArgsConstructor;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * @author dy
 * @date 2019/10/16
 */
@Service
@AllArgsConstructor
public class GoodsCoreServiceImpl implements GoodsCoreService {

    private final GoodsCollectionsService goodsCollectionsService;
    private final GoodsCategoryCoreService categoryCoreService;
    private final GoodsService goodsService;
    private final GoodsPriceRuleService goodsPriceRuleService;
    private final GoodsModelService goodsModelService;
    private final GoodsModelTotalService goodsModelTotalService;
    private final GoodsCategoryService goodsCategoryService;
    private final ScmGoodsUtils scmGoodsUtils;
    private final GoodsCoreBaseService baseService;

    @Override
    public IPage<GoodsExtDTO> findExtPage(Page<Goods> page, Goods goods) {
        GoodsSqlDTO query = new GoodsSqlDTO(goods);
        query.queryPage(page);
        // 分类及子分类
        if (StrUtil.isNotEmpty(goods.getGoodsCategoryId())) {
            Set<String> categoryList = goodsCategoryService.fetchChildren(goods.getGoodsCategoryId());
            query.setCategoryIds(categoryList.isEmpty() ? null : CollUtil.join(categoryList, StrUtil.COMMA));
        }
        IPage<GoodsExtDTO> result = goodsService.findPage(query);
        result.getRecords().forEach(GoodsTrans::priceRuleHandle);
        return result;
    }

    @Override
    @Cacheable(value = ScmCacheNameConstant.GOODS_INFO_DETAIL, key = "#page.current +'.'+ #page.size + #req")
    public IPage<GoodsVO> findInfoPage(Page<Goods> page, GoodsListReq req) {
        return findInfoPage(page, req, null);
    }

    /**
     * 查询商品详情列表 带收藏状态
     *
     * @param page
     * @param req
     * @param uid
     * @return
     */
    @Override
    public IPage<GoodsVO> findInfoPage(Page<Goods> page, GoodsListReq req, String uid) {
        if (CollUtil.isNotEmpty(req.getModelIds())) {
            // 采购商常用商品查询时使用
            List<String> goodsIdList = goodsModelService.list(Wrappers.<GoodsModel>query().lambda()
                    .in(GoodsModel::getId, req.getModelIds())
            ).stream().map(GoodsModel::getGoodsId).distinct().collect(Collectors.toList());
            req.setGoodsIds(goodsIdList);
        }
        // 调用缓存
        IPage<Goods> goodsPage = baseService.fetchPage(page, req);
        IPage<GoodsVO> result = new Page<>(goodsPage.getCurrent(), goodsPage.getSize(), goodsPage.getTotal());
        // 是否查询收藏状态
        boolean flag = StrUtil.isNotEmpty(uid);
        result.setRecords(goodsPage.getRecords().stream()
                .map(item -> {
                    // 调用缓存
                    GoodsVO goodsVO = baseService.fetchGoodsInfo(item, req.transToPricingReq());
                    if (flag) {
                        goodsVO.setCollectionStatus(goodsCollectionsService.fetchStatus(uid, item.getId()));
                    }
                    return goodsVO;
                }).collect(Collectors.toList()));
        return result;
    }

    /**
     * 商品收藏列表 (详情)
     *
     * @param page
     * @param req
     * @param uid  用户通行证
     * @return
     */
    @Override
    public IPage<GoodsVO> findCollectionsPage(Page page, GoodsListReq req, String uid) {
        GoodsSqlDTO query = new GoodsSqlDTO(req);
        query.setUid(uid);
        query.queryPage(page);
        // 分类及子分类
        if (StrUtil.isNotEmpty(req.getCategoryId())) {
            Set<String> categoryList = goodsCategoryService.fetchChildren(req.getCategoryId());
            query.setCategoryIds(categoryList.isEmpty() ? null : CollUtil.join(categoryList, StrUtil.COMMA));
        }
        // goods联查goodsCollections
        IPage<Goods> goodsPage = goodsCollectionsService.findPage(query);
        IPage<GoodsVO> result = new Page<>(goodsPage.getCurrent(), goodsPage.getSize(), goodsPage.getTotal());
        // 联查平台商品定价
        result.setRecords(goodsPage.getRecords().stream()
                .map(item -> {
                    // 调用缓存
                    GoodsVO goodsVO = baseService.fetchGoodsInfo(item, req.transToPricingReq());
                    goodsVO.setCollectionStatus(YesNoEnum.yes);
                    return goodsVO;
                }).collect(Collectors.toList()));
        return result;
    }

    /**
     * 查询商品详情
     *
     * @param goodsSn
     * @return
     */
    @Override
    public GoodsVO fetchInfo(String goodsSn, GoodsPricingReq pricingReq) {
        // 商品
        Goods goods = goodsService.fetchBySn(goodsSn);
        if (null == goods) {
            throw new ErrorDataException("商品有误");
        }
        // 调用 spring cache
        return baseService.fetchGoodsInfo(goods, pricingReq);
    }

    /**
     * 商品信息 带规格及类型列表
     *
     * @param goodsId
     * @return
     */
    @Override
    public GoodsVO fetchGoodsInfoById(String goodsId, GoodsPricingReq pricingReq) {
        Goods goods = checkedById(goodsId);
        // 调用 spring cache
        return baseService.fetchGoodsInfo(goods, pricingReq);
    }


    /**
     * 根据shopId 批量修改 商家id
     *
     * @param shopId
     * @param sourceMerchantId
     * @param targetMerchantId
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    @Caching(evict = {
            @CacheEvict(value = ScmCacheNameConstant.WECHAT_BUYER_GOODS_PAGE, allEntries = true),
            @CacheEvict(value = ScmCacheNameConstant.GOODS_CATEGORY_TREE, allEntries = true),
            @CacheEvict(value = ScmCacheNameConstant.GOODS_INFO_DETAIL, allEntries = true)
    })
    public void modifyMerchantByShopBatch(String shopId, String sourceMerchantId, String targetMerchantId) {
        // goods
        Goods goodsUpdate = new Goods();
        goodsUpdate.setMerchantId(targetMerchantId);
        goodsService.update(goodsUpdate, Wrappers.<Goods>query().lambda()
                .eq(Goods::getMerchantId, sourceMerchantId)
                .eq(Goods::getShopId, shopId));
        // goodsCategory
        GoodsCategory categoryUpdate = new GoodsCategory();
        categoryUpdate.setMerchantId(targetMerchantId);
        goodsCategoryService.update(categoryUpdate, Wrappers.<GoodsCategory>query().lambda()
                .eq(GoodsCategory::getMerchantId, sourceMerchantId)
                .eq(GoodsCategory::getShopId, shopId));
        // price
        GoodsPriceRule priceUpdate = new GoodsPriceRule();
        priceUpdate.setMerchantId(targetMerchantId);
        goodsPriceRuleService.update(priceUpdate, Wrappers.<GoodsPriceRule>query().lambda()
                .eq(GoodsPriceRule::getMerchantId, sourceMerchantId)
                .eq(GoodsPriceRule::getShopId, shopId));
        // scm_cms_goods_gifts 表有问题：未设置shopId
        // scm_cms_sale_rules 表有问题：未设置shopId
        // scm_cms_service_selection 表有问题：未设置shopId
    }

    /**
     * 新增商品
     *
     * @param req
     * @return
     */
    @Override
    public boolean addGoods(EditGoodsReq req) {
        // 商家店铺、分类、商品名称 验证
        req.setMerchantId(fetchMerchantIdCheckedShop(req.getShopId()));
        checkedGoodsName(req.getShopId(), req.getGoodsName(), null);
        checkedCategory(req.getShopId(), req.getGoodsCategoryId());
        baseService.addGoods(req);
        return true;
    }

    /**
     * 修改商品
     * 商品所属店铺不可改变
     *
     * @param req
     * @return
     */
    @Override
    public boolean modifyGoods(EditGoodsReq req) {
        Goods existGoods = checkedById(req.getGoodsId());
        // 商品所属店铺不可改变
        req.setShopId(existGoods.getShopId());
        req.setMerchantId(fetchMerchantIdCheckedShop(req.getShopId()));
        // 分类、商品名称 验证
        checkedGoodsName(req.getShopId(), req.getGoodsName(), existGoods.getId());
        checkedCategory(existGoods.getShopId(), req.getGoodsCategoryId());
        baseService.modifyGoods(req, existGoods);
        return true;
    }

    @Override
    public boolean deleteGoods(String goodsId) {
        checkedById(goodsId);
        // 是否有订单在使用该商品
        if (checkedGoodsUsed(goodsId)) {
            throw new ErrorDataException("该商品有订单在使用");
        }
        return baseService.deleteGoodsById(goodsId);
    }

    /**
     * 修改上下架
     *
     * @param goodsId
     * @param marketable
     * @return
     */
    @Override
    public boolean changeMarketable(String goodsId, boolean marketable) {
        Goods goods = checkedById(goodsId);
        int n = marketable ? 1 : 0;
        // 状态改变时更新
        if (goods.getIsMarketable() != null && goods.getIsMarketable() == n) {
            return true;
        }
        // salec商城修改上下架状态
        scmGoodsUtils.salecGoodsMarketableChange(goodsId, marketable);
        Goods update = new Goods();
        update.setId(goodsId);
        update.setIsMarketable(n);
        update.setGoodsStatus(fetchGoodsStatus(marketable, goods.getStorage()));
        return goodsService.updateById(update);
    }

    /**
     * 获取商品销售状态
     *
     * @param marketable 上下架
     * @param storage    库存
     * @return
     */
    private GoodsStatusEnum fetchGoodsStatus(boolean marketable, BigDecimal storage) {
        // 库存为0时 状态:已售罄
        return marketable ? (storage != null && storage.compareTo(BigDecimal.ZERO) == 0 ? GoodsStatusEnum.takeout : GoodsStatusEnum.sale) : GoodsStatusEnum.stock;
    }

    @Override
    public boolean changeSort(String goodsId, Integer sort) {
        Goods exist = checkedById(goodsId);
        if (exist.getSort() != null && exist.getSort().equals(sort.toString())) {
            return true;
        }
        Goods update = new Goods();
        update.setId(goodsId);
        update.setSort(sort.toString());
        return goodsService.updateById(update);
    }

    @Override
    public boolean changeStorage(String goodsId, BigDecimal storage) {
        Goods exist = checkedById(goodsId);
        if (exist.getStorage() != null && exist.getStorage().compareTo(storage) == 0) {
            return true;
        }
        Goods update = new Goods();
        update.setId(goodsId);
        update.setStorage(storage);
        return goodsService.updateById(update);
    }

    /**
     * 修改平台商品的商城启用状态
     *
     * @param goodsId
     * @param salecFlag
     * @return
     */
    @Override
    public boolean updateSalecStatus(String goodsId, YesNoEnum salecFlag) {
        Goods exist = checkedById(goodsId);
        if (exist.getSalecFlag() != null && exist.getSalecFlag() == salecFlag) {
            return true;
        }
        Goods update = new Goods();
        update.setId(goodsId);
        update.setSalecFlag(salecFlag);
        return goodsService.updateById(update);
    }


    /**
     * 根据供应商品创建平台商品
     *
     * @param goodsId
     * @param shopId  创建商品的平台店铺， 为空时默认平台店铺
     * @return
     */
    @Override
    public List<SupplierGoods> createGoodsBySupplyGoods(String goodsId, String shopId) {
        Goods goods = goodsService.getById(goodsId);
        if (null == goods) {
            throw new ErrorDataException("供应商品有误: 商品为空");
        }
        // 默认的平台店铺
        ShopExtDTO shop = scmGoodsUtils.fetchDefaultShop();
        if (StrUtil.equals(goods.getMerchantId(), shop.getMerchantId())) {
            throw new ErrorDataException("所选商品不能为平台商品");
        }
        // 保存的平台店铺 为空时默认平台店铺
        Shop platformShop;
        if (StrUtil.isEmpty(shopId)) {
            platformShop = shop;
        } else {
            platformShop = scmGoodsUtils.fetchShopById(shopId);
            if (null == platformShop) {
                throw new ErrorDataException("所选平台店铺有误");
            } else if (!StrUtil.equals(platformShop.getMerchantId(), shop.getMerchantId())) {
                throw new ErrorDataException("所选店铺需是平台店铺");
            }
        }
        // 所选店铺商品不能重名
        Goods exist = goodsService.fetchByName(platformShop.getId(), goods.getGoodsName());
        if (null != exist) {
            throw new ErrorDataException("所选商品有同名的平台商品");
        }
        GoodsPriceRule priceRule = goodsPriceRuleService.fetchByGoodsId(goodsId);
        if (null == priceRule) {
            throw new ErrorDataException("供应商品有误: 商品价格为空");
        }
        List<GoodsModelTotal> totalList = goodsModelTotalService.fetchTotalList(goodsId);
        if (totalList.isEmpty()) {
            throw new ErrorDataException("供应商品有误: 商品规格类型为空");
        }
        List<GoodsModel> modelList = goodsModelService.fetchModelList(goodsId);
        if (modelList.isEmpty()) {
            throw new ErrorDataException("供应商品有误: 商品规格为空");
        }
        GoodsCategory supplyGoodsCategory = goodsCategoryService.getById(goods.getGoodsCategoryId());
        if (modelList.isEmpty()) {
            throw new ErrorDataException("供应商品有误: 商品分类有误");
        }
        // 新增商品及规格
        GoodsDTO goodsDTO = new GoodsDTO(goods);
        goodsDTO.setShopId(platformShop.getId());
        goodsDTO.setMerchantId(platformShop.getMerchantId());
        // 新增商品的分类按名称匹配 无时在默认分类下
        GoodsCategory category = null == supplyGoodsCategory ? null : goodsCategoryService.fetchByName(platformShop.getId(), supplyGoodsCategory.getName());
        if (null == category) {
            Shop categoryShop = new Shop();
            categoryShop.setId(platformShop.getId());
            categoryShop.setMerchantId(platformShop.getMerchantId());
            category = categoryCoreService.fetchDefaultCategory(categoryShop);
        }
        goodsDTO.setCategoryId(category.getId());
        goodsDTO.copyPriceRule(priceRule);
        goodsDTO.setModelList(modelList.stream()
                .map(GoodsModelDTO::new)
                .collect(Collectors.toList()));
        // 规格类型名称转换
        goodsDTO.setAttrMap(GoodsTrans.fetchAttrTypeMap(totalList));
        // 新增
        goodsDTO = baseService.saveGoodsWithDetail(goodsDTO);
        Map<String, GoodsModel> modelMap = modelList.stream()
                .collect(Collectors.toMap(GoodsModel::getModelName, Function.identity()));
        // 规格名称匹配创建关联
        return goodsDTO.getModelList().stream()
                .map(model -> SupplyGoodsTrans.initRelation(model, modelMap.get(model.getModelName())))
                .collect(Collectors.toList());
    }

    /**
     * 特殊新增商品（不为平台店铺）
     * 商品分类为选择的平台店铺分类
     * 分类按名称匹配，找不到时新增分类及父级
     *
     * @param req
     * @return
     */
    @Override
    public boolean specialAddGoods(EditGoodsReq req) {
        Merchant pm = scmGoodsUtils.fetchPlatformMerchant();
        // 商家店铺、商品名称 验证
        String merchantId = fetchMerchantIdCheckedShop(req.getShopId());
        if (StrUtil.equals(pm.getId(), merchantId)) {
            throw new ErrorDataException("新增商品失败：商品所属店铺为平台店铺，不可使用该功能");
        }
        req.setMerchantId(merchantId);
        checkedGoodsName(req.getShopId(), req.getGoodsName(), null);

        String platformCategoryId = req.getGoodsCategoryId();
        // 匹配商品店铺分类
        req.setGoodsCategoryId(baseService.specialCategoryFetch(platformCategoryId, pm.getId(), req.getShopId(), req.getMerchantId()));
        Goods goods = baseService.addGoods(req);
        // 处理分类关联
        baseService.updateSpecialCategory(goods.getGoodsCategoryId(), platformCategoryId);
        return true;
    }

    /**
     * 商品修改
     * 商品所属店铺不可改变
     *
     * @param req
     * @return
     */
    @Override
    public boolean specialModifyGoods(EditGoodsReq req) {
        Merchant pm = scmGoodsUtils.fetchPlatformMerchant();
        Goods existGoods = checkedById(req.getGoodsId());
        // 商品所属店铺不可改变
        req.setShopId(existGoods.getShopId());
        // 商家店铺、分类、商品名称 验证
        String merchantId = fetchMerchantIdCheckedShop(req.getShopId());
        if (StrUtil.equals(pm.getId(), merchantId)) {
            throw new ErrorDataException("修改商品失败：商品所属店铺为平台店铺，不可使用该功能");
        }
        req.setMerchantId(merchantId);
        checkedGoodsName(req.getShopId(), req.getGoodsName(), existGoods.getId());
        String platformCategoryId = req.getGoodsCategoryId();
        // 匹配商品店铺分类
        req.setGoodsCategoryId(baseService.specialCategoryFetch(platformCategoryId, pm.getId(), req.getShopId(), req.getMerchantId()));
        Goods goods = baseService.modifyGoods(req, existGoods);
        // 处理分类关联
        baseService.updateSpecialCategory(goods.getGoodsCategoryId(), platformCategoryId);
        return true;
    }

    // 内部使用验证方法

    /**
     * 验证商品是否有在使用
     *
     * @param goodsId
     * @return
     */
    private boolean checkedGoodsUsed(String goodsId) {
        Set<String> modelIdSet = goodsModelService.fetchModelList(goodsId).stream()
                .map(GoodsModel::getId).collect(Collectors.toSet());
        // 采购订单
        int num = scmGoodsUtils.countBuyOrderBuyGoods(modelIdSet);
        if (num > 0) {
            return true;
        }
        // 供应订单
        num = scmGoodsUtils.countSupplyOrderBuyGoods(modelIdSet);
        if (num > 0) {
            return true;
        }
        // 销售订单
        num = scmGoodsUtils.countSalesOrderBuyGoods(modelIdSet);
        return num > 0;
    }

    /**
     * 商品验证
     *
     * @param goodsId
     * @return
     */
    private Goods checkedById(String goodsId) {
        Goods goods = goodsService.getById(goodsId);
        if (null == goods) {
            throw new ErrorDataException("商品有误");
        }
        return goods;
    }

    /**
     * 验证店铺获取其商家id
     *
     * @param shopId
     * @return
     */
    private String fetchMerchantIdCheckedShop(String shopId) {
        Shop shop = scmGoodsUtils.fetchShopById(shopId);
        if (null == shop) {
            throw new ErrorDataException("店铺有误");
        }
        return shop.getMerchantId();
    }

    /**
     * 分类验证
     *
     * @param shopId
     * @param categoryId
     * @return
     */
    private GoodsCategory checkedCategory(String shopId, String categoryId) {
        GoodsCategory category = goodsCategoryService.getById(categoryId);
        if (null == category || !StrUtil.equals(category.getShopId(), shopId)) {
            throw new ErrorDataException("商品分类有误");
        }
        return category;
    }

    /**
     * 商品同名验证
     *
     * @param shopId
     * @param goodsName
     * @param existGoodsId
     */
    private void checkedGoodsName(String shopId, String goodsName, String existGoodsId) {
        Goods exist = goodsService.list(Wrappers.<Goods>query().lambda()
                .eq(Goods::getShopId, shopId)
                .eq(Goods::getGoodsName, goodsName)
                .ne(StrUtil.isNotEmpty(existGoodsId), Goods::getId, existGoodsId)
        ).stream().findFirst().orElse(null);
        if (null != exist) {
            throw new ErrorDataException("已存在同名商品");
        }
    }

}
